package concurrent;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * CyclicBarrier通常称为循环屏障。它和CountDownLatch很相似，都可以使线程先等待然后再执行。
 * 不过CountDownLatch是使一批线程等待另一批线程执行完后再执行；
 * 而CyclicBarrier只是使等待的线程达到一定数目后再让它们继续执行
 * CyclicBarrier内部也有一个计数器
 * 每调用一次await()方法都将使阻塞的线程数+1，只有阻塞的线程数达到设定值时屏障才会打开，允许阻塞的所有线程继续执行
 * 内部有一个人把规则破坏了（等待超时），其他人都不按规则来了，不会等待了
 * @author jack.wu
 * @since 2019/12/26 17:47
 */
public class CyclicBarrierDemo {
    private static CyclicBarrier barrier = new CyclicBarrier(10);

    public static class T extends Thread {

        int sleep;

        public T(String name, int sleep) {
            super(name);
            this.sleep = sleep;
        }

        @Override
        public void run() {
            try {
                TimeUnit.SECONDS.sleep(sleep);
                long startTime = System.currentTimeMillis();
                // 模拟等待员工都到齐才能继续
                barrier.await();
                long endTime = System.currentTimeMillis();
                System.out.println(this.getName() + "，sleep:" + this.sleep + "等待了" + (endTime - startTime));
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new T("员工"+i,i).start();
        }
    }

}

/**
 * 最后一个到模拟赔礼
 */
class CyclicBarrierDemo1 {
    private static CyclicBarrier barrier = new CyclicBarrier(10,()->{
        try {
            // 模拟最后一个人给赔礼倒酒
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"说，让大家久等了");
    });

    public static class T extends Thread {

        int sleep;

        public T(String name, int sleep) {
            super(name);
            this.sleep = sleep;
        }

        @Override
        public void run() {
            try {
                TimeUnit.SECONDS.sleep(sleep);
                long startTime = System.currentTimeMillis();
                // 模拟等待员工都到齐才能继续
                barrier.await();
                long endTime = System.currentTimeMillis();
                System.out.println(this.getName() + "，sleep:" + this.sleep + "等待了" + (endTime - startTime));
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new T("员工"+i,i).start();
        }
    }

}

/**
 * 重建规则
 */
class CyclicBarrierDemo2 {
    private static CyclicBarrier barrier = new CyclicBarrier(10);
    private static boolean rule = false;

    public static class T extends Thread {

        int sleep;

        public T(String name, int sleep) {
            super(name);
            this.sleep = sleep;
        }

        @Override
        public void run() {
            try {
                TimeUnit.SECONDS.sleep(sleep);
                long startTime = System.currentTimeMillis();
                System.out.println(this.getName()+"到了");
                if (rule){
                    barrier.await();
                }else {
                    // 模拟员工1等不及了
                    if ("员工1".equals(this.getName())){
                        barrier.await(3,TimeUnit.SECONDS);
                    }else {
                        barrier.await();
                    }
                }
                // 模拟等待员工都到齐才能继续
                long endTime = System.currentTimeMillis();
                System.out.println(this.getName() + "，sleep:" + this.sleep + "等待了" + (endTime - startTime));
            } catch (InterruptedException | BrokenBarrierException | TimeoutException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            new T("员工"+i,i).start();
        }
        // 15秒之后按照规则
        TimeUnit.SECONDS.sleep(15);
        barrier.reset();
        rule = true;
        System.out.println("---------按照规则----------");
        for (int i = 0; i < 10; i++) {
            new T("员工"+i,i).start();
        }
    }

}
